package com.yxinsur.imc.business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxinsur.imc.business.domain.*;
import com.yxinsur.imc.business.enums.*;
import com.yxinsur.imc.business.excel.CommDetailsTemplate;
import com.yxinsur.imc.business.excel.RecnTemplate;
import com.yxinsur.imc.business.form.RecnSearchForm;
import com.yxinsur.imc.business.mapper.*;
import com.yxinsur.imc.business.service.IImcRecnExpenseService;
import com.yxinsur.imc.business.service.IImcRecnService;
import com.yxinsur.imc.business.vo.*;
import com.yxinsur.imc.common.annotation.Excel;
import com.yxinsur.imc.common.core.domain.AjaxResult;
import com.yxinsur.imc.common.core.domain.entity.SysOrg;
import com.yxinsur.imc.common.utils.DateUtils;
import com.yxinsur.imc.common.utils.SecurityUtils;
import com.yxinsur.imc.common.utils.StringUtils;
import com.yxinsur.imc.common.utils.poi.ExcelUtil;
import com.yxinsur.imc.system.mapper.SysOrgMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 首年手续费对账单 Service业务层处理
 *
 * @author imc
 * @date 2021-05-12
 */
@Service
public class ImcRecnServiceImpl extends ServiceImpl<ImcRecnMapper, ImcRecn> implements IImcRecnService {

    private static final Logger logger = LoggerFactory.getLogger(ImcRecnServiceImpl.class);

    @Autowired
    private SysOrgMapper sysOrgMapper;
    @Autowired
    private ImcRecnMapper imcRecnMapper;
    @Autowired
    private ImcContractMapper imcContractMapper;
    @Autowired
    private ImcOrderRiskMapper imcOrderRiskMapper;
    @Autowired
    private ImcContractOrgMapper imcContractOrgMapper;
    @Autowired
    private ImcContractInsMapper imcContractInsMapper;
    @Autowired
    private ImcRecnDetailsMapper imcRecnDetailsMapper;
    @Autowired
    private ImcRecnSupplierDetailsMapper imcRecnSupplierDetailsMapper;
    @Autowired
    private ImcOrderMapper imcOrderMapper;
    @Autowired
    private ImcRepoInsMapper imcRepoInsMapper;
    @Autowired
    private ImcContractExpenseMapper imcContractExpenseMapper;
    @Autowired
    private ImcContractExpenseRateMapper imcContractExpenseRateMapper;
    @Autowired
    private IImcRecnExpenseService recnExpenseService;

    /**
     * 生成手续费对账单
     * <p>
     * a.写入主表
     * b.根据条件查询[order_risk]表
     * c.写入L表（默认值：核对结果（短账LS）手续费处理结果（忽略不处理C）手续费是否结算（N））
     *
     * @param settleMonth           选择月度
     * @param supplierId            供应商ID
     * @param contractIdList        协议ID集合
     * @param contractExpenseIdList 协议费用科目ID集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult createImcRecn(String settleMonth, Long supplierId, List<Long> contractIdList, List<Long> contractExpenseIdList) {
        // a.写入主表
        ImcRecn imcRecn = new ImcRecn();
        imcRecn.setSupplierId(supplierId);
        imcRecn.setContractIds(StrUtil.join(",", contractIdList));
        imcRecn.setContractExpenseIds(StrUtil.join(",", contractExpenseIdList));
        imcRecn.setSettleMonth(settleMonth + "-01");
        imcRecn.setRecnStatus(RecnStatusEnum.A.getCode());
        imcRecn.setTotalPrem(new BigDecimal("0.0"));
        imcRecn.setTotalFee(new BigDecimal("0.0"));
        imcRecn.setTenantId(SecurityUtils.getTenantId());
        imcRecn.setCreateId(SecurityUtils.getUsername());
        imcRecn.setCreateTime(DateUtil.date());
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        imcRecn.setIsDeleted(IsDeletedEnum.N.getCode());
        this.imcRecnMapper.insertImcRecn(imcRecn);

        // b.根据条件查询[order_risk]表
        // 查询[order_risk]的条件：协议关联的机构ID集合（本机构及下属所有子机构）
        List<ContractOrgVo> selectImcContractOrgList = imcContractOrgMapper.listImcContractOrgByContractIds(contractIdList);
        List<Long> imcContractOrgIdList = selectImcContractOrgList.stream().map(ContractOrgVo::getOrgId).collect(Collectors.toList());
        List<SysOrg> sysOrgList = CollUtil.newArrayList();
        for (ContractOrgVo item : selectImcContractOrgList) {
            List<SysOrg> childrenSysOrgList = sysOrgMapper.selectChildrenOrgById(item.getOrgId());
            sysOrgList.addAll(childrenSysOrgList);
        }
        List<Long> orgIdList = sysOrgList.stream().map(SysOrg::getOrgId).collect(Collectors.toList());
        orgIdList.addAll(imcContractOrgIdList);

        // 查询[order_risk]表的条件：协议关联的产品ID集合
        List<RepoInsVo> repoInsVoList = imcContractInsMapper.listImcContractInsByContractIds(contractIdList);
        List<Long> repoInsIdList = repoInsVoList.stream().map(RepoInsVo::getId).collect(Collectors.toList());

        List<ImcRecnDetails> recnDetailsList = CollUtil.newArrayList();
        for (Long contractExpenseId : contractExpenseIdList) {
            // 协议费用科目口径表达式 SQL
            String criteriaSql = recnExpenseService.getCriteriaSQL(settleMonth, contractExpenseId);
            // 根据{机构}{产品}{协议费用科目口径表达式SQL}{协议费用科目ID}查询[order_risk]表
            List<ImcRecnDetails> imcOrderRiskList = imcOrderRiskMapper.listRecnOrderRiskList(orgIdList, repoInsIdList, criteriaSql, contractExpenseId, SecurityUtils.getTenantId());
            recnDetailsList.addAll(imcOrderRiskList);
        }

        if (CollUtil.isEmpty(recnDetailsList)) {
            return AjaxResult.success("选择月度：" + settleMonth + " 无可结算保单");
        }

        // 设置费用科目手续费、费用科目手续费率
        for (ImcRecnDetails item : recnDetailsList) {
            QueryWrapper<ImcContractExpenseRate> checkWrapper = setCheckWrapper(item);
            logger.info("费用科目费率查询条件：" + JSONUtil.parse(checkWrapper));
            List<ImcContractExpenseRate> rateList = this.imcContractExpenseRateMapper.selectList(checkWrapper);
            // 如果根据条件查询出的费率为多条，则取最后一条费率数据，否则将该字段置空
            item.setFeeRate(rateList.size() > 0 ? rateList.get(rateList.size() - 1).getRate() : null);
            logger.info("费用科目费率查询结果：" + item.getFeeRate());
            String feeCalcRule = item.getFeeCalcRule();
            if (ObjectUtil.isNotNull(item.getFeeRate())) {
                if (RecnFeeCalcRuleEnum.A.getCode().equals(feeCalcRule) || StrUtil.isBlank(feeCalcRule)) {
                    item.setFee(item.getPrem().multiply(item.getFeeRate()));
                }
                if (RecnFeeCalcRuleEnum.B.getCode().equals(feeCalcRule)) {
                    item.setFee(item.getPrem().multiply(item.getFeeRate()).multiply(item.getSupplierStandRate()));
                }
                if (RecnFeeCalcRuleEnum.C.getCode().equals(feeCalcRule)) {
                    item.setFee(item.getPrem().multiply(item.getFeeRate()).multiply(item.getYearScale()));
                }
            } else {
                item.setFee(null);
            }
        }

        // c.写入L表
        List<ImcRecnDetails> lList = CollUtil.newArrayList();
        for (ImcRecnDetails item : recnDetailsList) {
            item.setRecnId(imcRecn.getId());
            item.setSettleMonth(settleMonth);
            item.setRecnCheckResult(RecnCheckResultEnum.LS.getCode());
            item.setRecnHandlingResult(RecnHandlingResultEnum.C.getCode());
            item.setIsFeeEq(YesOrNoEnum.N.name());
            item.setTenantId(SecurityUtils.getTenantId());
            item.setCreateId(SecurityUtils.getUsername());
            item.setCreateTime(DateUtil.date());
            item.setUpdateId(SecurityUtils.getUsername());
            item.setUpdateTime(DateUtil.date());
            item.setIsDeleted(IsDeletedEnum.N.getCode());
            lList.add(item);
        }
        this.imcRecnDetailsMapper.insertImcRecnDetailsList(lList);
        return AjaxResult.success();
    }

    /**
     * 导入S表并核对
     * <p>
     * 1.上传文件，并获取上传文件的路径
     * 2.更新主表[supplier_file_url]字段、主表状态=>已上传
     * 3.写入S表，如校验未通过则返回错误信息
     * 4.核对处理
     *
     * @param file   上传文件
     * @param recnId 主表ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult importAndCheck(MultipartFile file, Long recnId) throws Exception {
        // 上传文件
        ExcelUtil<RecnTemplate> util = new ExcelUtil<>(RecnTemplate.class);
        List<RecnTemplate> templateList = util.importExcel(file.getInputStream());
        if (CollUtil.isEmpty(templateList)) {
            return AjaxResult.error("请检查上传数据是否有效！");
        }
        // 校验：上传文件模板校验是否为正确模板
        String[] titleStrArr = new String[]{"序号", "供应商保单号*", "供应商险种代码*", "供应商险种名称*", "供应商承保日期*", "供应商回执签收日期*",
                "供应商缴费期限*", "供应商缴费期数*", "供应商保单年度*", "供应商保费*", "备注"};
        boolean isRightTemplate = this.recnExpenseService.importTemplateVerify(file, titleStrArr, "首年供应商手续费明细");
        if (!isRightTemplate) {
            return AjaxResult.error("请检查上传模板是否仅包含"+Arrays.toString(titleStrArr)+"字段");
        }
        String filename = util.exportExcel(templateList, "SUPPLIER_RECN").get("msg").toString();

        // 更新主表
        ImcRecn imcRecn = imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isNull(imcRecn)) {
            return AjaxResult.error("该手续费对账单不存在，请核实后再上传！");
        }
        imcRecn.setSupplierFileUrl(filename);
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        imcRecn.setRecnStatus(RecnStatusEnum.B.getCode());
        imcRecnMapper.updateImcRecn(imcRecn);
        String settleMonth = imcRecn.getSettleMonth().substring(0, 7);
        Long supplierId = imcRecn.getSupplierId();

        //  写入S表，如校验未通过则返回错误信息
        Map<String, Object> rtnMap = importRecnSupplierDetails(recnId, settleMonth, supplierId, templateList);
        if (ObjectUtil.isNotNull(rtnMap.get(RecnFileStatusEnum.FAIL.getCode()))) {
            List<RecnTemplate> errorTemplateList = (List<RecnTemplate>) rtnMap.get(RecnFileStatusEnum.FAIL.getCode());
            return util.exportErrorExcel(errorTemplateList, "上传供应商手续费信息错误明细", "上传文件存在错误信息，请处理后再重新上传");
        }

        // 核对处理
        if (ObjectUtil.isNotNull(rtnMap.get(RecnFileStatusEnum.SUCCESS.getCode()))) {
            // L表数据（因L：S为多对一关系，因此需对L表数据做行转列处理）
            List<RecnDetailsVo> lList = this.recnExpenseService.rowTransColumnByOrderRiskId(imcRecnDetailsMapper.listRecnDetailsCheckItemVoByRecnId(recnId, SecurityUtils.getTenantId()));
            if (CollUtil.isEmpty(lList)) {
                return AjaxResult.error("系统手续费信息为空，请先生成系统手续费信息");
            }
            // S表数据
            List<RecnDetailsVo> sList = imcRecnSupplierDetailsMapper.listRecnDetailsCheckItemVoByRecnId(recnId, SecurityUtils.getTenantId());
            if (CollUtil.isEmpty(sList)) {
                return AjaxResult.error("供应商手续费信息为空，请先上传供应商手续费信息");
            }
            // 核对
            checkOfLsSuccess(recnId, lList, sList);
            checkOfLs(recnId, lList, sList);
            checkOfSl(lList, sList);
        }
        return AjaxResult.success();
    }

    /**
     * 查询主表列表
     *
     * @param searchForm RecnSearchForm
     * @return 主表集合
     */
    @Override
    public List<RecnVo> listImcRecn(RecnSearchForm searchForm) {
        String settleMonth = StrUtil.isNotBlank(searchForm.getSettleMonth()) ? searchForm.getSettleMonth() + "-01" : null;
        String authOrgIds = SecurityUtils.getLoginUser().getAuthOrgIds();
        String[] orgIdArr = authOrgIds.split(",");
        List<RecnVo> recnVoList = this.imcRecnMapper.listImcRecn(settleMonth, orgIdArr, searchForm.getSupplierId(), SecurityUtils.getTenantId());
        // 前端展示字段处理
        recnVoList.forEach(recnVo -> {
            List<String> contractIdStrList = CollUtil.newArrayList(StrUtil.split(recnVo.getContractIds(), ","));
            List<Long> contractIdList = contractIdStrList.stream().map(Long::parseLong).collect(Collectors.toList());
            StringBuilder contractName = new StringBuilder();
            contractIdList.forEach(contractId -> {
                ContractVo contractVo = this.imcContractMapper.getImcContractById(contractId);
                contractName.append(contractVo.getContractName()).append(",");
            });
            recnVo.setContractName(contractName.deleteCharAt(contractName.length() - 1).toString());
            List<String> expenseIdStr = CollUtil.newArrayList(StrUtil.split(recnVo.getContractExpenseIds(), ","));
            List<Long> expenseIdList = expenseIdStr.stream().map(Long::parseLong).collect(Collectors.toList());
            StringBuilder expenseName = new StringBuilder();
            expenseIdList.forEach(expenseId -> {
                ContractExpenseVo contractExpenseVo = this.imcContractExpenseMapper.getImcContractExpenseVoById(expenseId);
                expenseName.append(contractExpenseVo.getContractId()).append("-").append(contractExpenseVo.getExpenseName()).append(",");
            });
            recnVo.setExpenseName(expenseName.deleteCharAt(expenseName.length() - 1).toString());
        });
        return recnVoList;
    }

    /**
     * 查询核对结果列表
     *
     * @param searchForm RecnSearchForm
     * @return List<RecnDetailsCheckVo>
     */
    @Override
    public List<RecnDetailsCheckVo> listCheckResultList(RecnSearchForm searchForm) {
        Long recnId = searchForm.getRecnId();
        String recnCheckResult = searchForm.getRecnCheckResult();
        String recnHandlingResult = searchForm.getRecnHandlingResult();
        // 平账&短账数据（需要行转列：以{orderRiskId}为唯一标识，将动态列补充到数据中）
        List<RecnDetailsCheckVo> beforeTransList = this.imcRecnMapper.listCheckOfLsSuccessAndLs(recnId, SecurityUtils.getTenantId());
        // 防止 feeL 的值在行转列为空时异常，将空值替换为 0.0
        beforeTransList.forEach(item -> item.setFeeL(ObjectUtil.isNotNull(item.getFeeL()) ? item.getFeeL() : new BigDecimal("0.0")));
        List<RecnDetailsCheckVo> recnDetailsCheckVoList = rowTransColumn(beforeTransList);
        // 长账数据
        List<RecnDetailsCheckVo> slList = this.imcRecnMapper.listCheckOfSL(recnId, SecurityUtils.getTenantId());
        recnDetailsCheckVoList.addAll(slList);
        // 条件筛选
        if (StrUtil.isNotBlank(recnCheckResult)) {
            recnDetailsCheckVoList = recnDetailsCheckVoList.stream().filter(item -> item.getRecnCheckResult().equals(recnCheckResult)).collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(recnHandlingResult)) {
            recnDetailsCheckVoList = recnDetailsCheckVoList.stream().filter(item -> item.getRecnHandlingResult().equals(recnHandlingResult)).collect(Collectors.toList());
        }
        return recnDetailsCheckVoList;
    }

    /**
     * 导出核对结果
     * <p>
     * 1.数据处理：平账、短账数据（行转列）+ 长账数据
     * 2.根据协议费用科目（动态列）处理表头
     * 3.整理导出数据
     *
     * @param searchForm RecnSearchForm
     * @return List<RecnTemplate>
     */
    @Override
    public List<RecnTemplate> export(RecnSearchForm searchForm) throws NoSuchFieldException, IllegalAccessException {
        makeCommDetailsInit();
        Long recnId = searchForm.getRecnId();
        String recnCheckResult = searchForm.getRecnCheckResult();
        String recnHandlingResult = searchForm.getRecnHandlingResult();

        // 平账&短账数据（需要行转列：以{orderRiskId}为唯一标识，将动态列补充到数据中）
        List<RecnDetailsCheckVo> beforeTransList = this.imcRecnMapper.listCheckOfLsSuccessAndLs(recnId, SecurityUtils.getTenantId());
        // 防止 feeL 的值在行转列为空时异常，将空值替换为 0.0
        beforeTransList.forEach(item -> item.setFeeL(ObjectUtil.isNotNull(item.getFeeL()) ? item.getFeeL() : new BigDecimal("0.0")));
        List<RecnDetailsCheckVo> recnDetailsCheckVoList = rowTransColumn(beforeTransList);
        // 长账数据
        List<RecnDetailsCheckVo> slList = this.imcRecnMapper.listCheckOfSL(recnId, SecurityUtils.getTenantId());
        recnDetailsCheckVoList.addAll(slList);

        // 条件筛选
        if (StrUtil.isNotBlank(recnCheckResult)) {
            recnDetailsCheckVoList = recnDetailsCheckVoList.stream().filter(item -> item.getRecnCheckResult().equals(recnCheckResult)).collect(Collectors.toList());
        }
        if (StrUtil.isNotBlank(recnHandlingResult)) {
            recnDetailsCheckVoList = recnDetailsCheckVoList.stream().filter(item -> item.getRecnHandlingResult().equals(recnHandlingResult)).collect(Collectors.toList());
        }

        // 根据协议费用科目（动态列）处理表头
        List<Long> contractExpenseIdList = new ArrayList<Long>();
        ImcRecn imcRecn = imcRecnMapper.getImcRecnById(recnId);
        String contractExpenseIds = imcRecn.getContractExpenseIds();
        String[] contractExpenseIdArr = contractExpenseIds.split(",");
        for (String item : contractExpenseIdArr) {
            contractExpenseIdList.add(Long.valueOf(item));
        }
        Collections.sort(contractExpenseIdList);
        Map<String, String> titleMap = this.recnExpenseService.setTitle(contractExpenseIdList);
        this.recnExpenseService.setCommDetailsAnnotation(titleMap);

        // 整理导出数据
        List<RecnTemplate> recnTemplateList = CollUtil.newArrayList();
        for (RecnDetailsCheckVo recnDetailsCheckVo : recnDetailsCheckVoList) {
            RecnTemplate temple = new RecnTemplate();
            BeanUtils.copyProperties(recnDetailsCheckVo, temple);
            Map<String, Object> dynamicFieldMap = recnDetailsCheckVo.getDynamicFieldMap();
            if (ObjectUtil.isNotNull(dynamicFieldMap) && contractExpenseIdList.size() > 0 && contractExpenseIdList.size() <= 5) {
                temple.setContractExpenseA((String) recnDetailsCheckVo.getDynamicFieldMap().get(contractExpenseIdList.get(0).toString()));
                temple.setContractExpenseB(contractExpenseIdList.size() > 1 ? (String) dynamicFieldMap.get(contractExpenseIdList.get(1).toString()) : null);
                temple.setContractExpenseC(contractExpenseIdList.size() > 2 ? (String) dynamicFieldMap.get(contractExpenseIdList.get(2).toString()) : null);
                temple.setContractExpenseD(contractExpenseIdList.size() > 3 ? (String) dynamicFieldMap.get(contractExpenseIdList.get(3).toString()) : null);
                temple.setContractExpenseE(contractExpenseIdList.size() > 4 ? (String) dynamicFieldMap.get(contractExpenseIdList.get(4).toString()) : null);
                temple.setContractExpenseF(contractExpenseIdList.size() > 5 ? (String) dynamicFieldMap.get(contractExpenseIdList.get(5).toString()) : null);
                temple.setContractExpenseG(contractExpenseIdList.size() > 6 ? (String) dynamicFieldMap.get(contractExpenseIdList.get(6).toString()) : null);
            }
            recnTemplateList.add(temple);
        }
        return recnTemplateList;
    }

    //将模板类恢复到初始状态
    private void makeCommDetailsInit() throws NoSuchFieldException, IllegalAccessException {
        Field field1 = RecnTemplate.class.getDeclaredField("contractExpenseA");
        Field field2 = RecnTemplate.class.getDeclaredField("contractExpenseB");
        Field field3 = RecnTemplate.class.getDeclaredField("contractExpenseC");
        Field field4 = RecnTemplate.class.getDeclaredField("contractExpenseD");
        Field field5 = RecnTemplate.class.getDeclaredField("contractExpenseE");
        Field field6 = RecnTemplate.class.getDeclaredField("contractExpenseF");
        Field field7 = RecnTemplate.class.getDeclaredField("contractExpenseG");

        Field[] fields = new Field[]{field1,field2,field3,field4,field5,field6,field7};
        for (Field field : fields) {
            Excel excel = field.getAnnotation(Excel.class);//获取字段上的Excel注解
            InvocationHandler h = Proxy.getInvocationHandler(excel);
            Field hField = h.getClass().getDeclaredField("memberValues");
            hField.setAccessible(true);
            Map memberValues = (Map) hField.get(h);
            memberValues.put("type",Excel.Type.EXPORT);
            memberValues.put("name","默认字段");
            hField.setAccessible(false);
        }
    }

    /**
     * 批量处理核对结果
     * 注：只有平账记录结果才可以对核对结果进行处理
     *
     * @param searchForm RecnSearchForm
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult disposeImcRecn(RecnSearchForm searchForm) {
        logger.info("手续费对账单-批量处理核对结果：{}", JSONUtil.parse(searchForm));
        String recnHandlingResult = searchForm.getRecnHandlingResult();
        List<Long> supplierDetailsIdList = searchForm.getRecnSupplierDetailsIdList();
        if (CollUtil.isEmpty(supplierDetailsIdList)) {
            return AjaxResult.error("请先选择一条核对结果进行处理！");
        }
        List<ImcRecnDetails> lList = CollUtil.newArrayList();
        List<ImcRecnSupplierDetails> sList = CollUtil.newArrayList();

        // 批量更新L表、S表
        for (Long item : supplierDetailsIdList) {
            List<ImcRecnDetails> detailsList = this.imcRecnDetailsMapper.listRecnDetailsByRecnSupplierDetailsId(item, SecurityUtils.getTenantId());
            for (ImcRecnDetails lDetails : detailsList) {
                if (!RenewalRecnCheckResultEnum.L_SUCCESS.getCode().equals(lDetails.getRecnCheckResult())) {
                    return AjaxResult.error("只有平账记录才可以对核对结果进行处理，请重新选择！");
                }
                lDetails.setRecnHandlingResult(recnHandlingResult);
                lDetails.setUpdateId(SecurityUtils.getUsername());
                lDetails.setUpdateTime(DateUtil.date());
                lList.add(lDetails);
            }
            ImcRecnSupplierDetails sDetails = this.imcRecnSupplierDetailsMapper.getImcRecnSupplierDetailsById(item);
            sDetails.setRecnHandlingResult(recnHandlingResult);
            sDetails.setUpdateId(SecurityUtils.getUsername());
            sDetails.setUpdateTime(DateUtil.date());
            sList.add(sDetails);
        }
        this.imcRecnDetailsMapper.updateImcRecnDetailsListOfDispose(lList);
        this.imcRecnSupplierDetailsMapper.updateSupplierDetailsListOfDispose(sList);
        return AjaxResult.success();
    }

    /**
     * 提交审核
     *
     * @param recnId 主表ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult auditImcRecn(Long recnId) {
        logger.info("手续费账单-提交审核：{}", recnId);
        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isNull(imcRecn)) {
            return AjaxResult.error("该手续费对账单不存在");
        }
        // 校验：是否存在长账
        boolean isExistSl = this.imcRecnSupplierDetailsMapper.countOfSupplierDetailsByRecnId(recnId, RecnCheckResultEnum.SL.getCode(), SecurityUtils.getTenantId()) > 0;
        if (isExistSl) {
            return AjaxResult.error("该手续费对账单存在长账，请处理后再提交审核");
        }
        // 校验：是否存在平账
        ImcRecnDetails imcRecnDetails = new ImcRecnDetails();
        imcRecnDetails.setRecnId(recnId);
        imcRecnDetails.setTenantId(SecurityUtils.getTenantId());
        imcRecnDetails.setRecnCheckResult(RecnCheckResultEnum.L_SUCCESS.getCode());
        List<ImcRecnDetails> imcRecnDetailsList = this.imcRecnDetailsMapper.selectImcRecnDetailsList(imcRecnDetails);
        if (ObjectUtil.isEmpty(imcRecnDetailsList)) {
            return AjaxResult.error("该手续费对账单不存在平账，请处理后再提交审核");
        }
        // 更新主表：结算状态=>审核中
        imcRecn.setRecnStatus(RecnStatusEnum.C.getCode());
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        this.imcRecnMapper.updateImcRecn(imcRecn);
        return AjaxResult.success();
    }

    /**
     * 审核通过
     *
     * @param recnId         主表ID
     * @param recnSearchForm RecnSearchForm
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult auditApproved(Long recnId, RecnSearchForm recnSearchForm) {
        logger.info("手续费账单-审核通过：{}", recnId);
        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isNull(imcRecn)) {
            return AjaxResult.error("该手续费对账单不存在");
        }
        // 校验：是否存在长账情况
        boolean isExistSl = this.imcRecnSupplierDetailsMapper.countOfSupplierDetailsByRecnId(recnId, RecnCheckResultEnum.SL.getCode(), SecurityUtils.getTenantId()) > 0;
        if (isExistSl) {
            return AjaxResult.error("该手续费对账单存在长账，请处理后再审核通过");
        }

        BigDecimal totalFee = new BigDecimal("0.0");
        BigDecimal totalPrem = new BigDecimal("0.0");

        // 批量更新[order_risk]表：手续费是否结算状态=>已结算
        List<ImcRecnDetails> lList = this.imcRecnDetailsMapper.selectImcRecnDetailsByRecnId(recnId, SecurityUtils.getTenantId());
        List<ImcOrderRisk> imcOrderRiskList = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(lList)) {
            for (ImcRecnDetails item : lList) {
                // 平账数据才可以被审核通过
                if (RecnCheckResultEnum.L_SUCCESS.getCode().equals(item.getRecnCheckResult())) {
                    ImcOrderRisk imcOrderRisk = this.imcOrderRiskMapper.getImcOrderRiskById(item.getOrderRiskId());
                    if (StrUtil.isBlank(imcOrderRisk.getSettleMonth())) {
                        // 费用科目中配置的基础手续费为是时，更新险种手续费是否结算状态
                        ContractExpenseVo contractExpenseVo = this.imcContractExpenseMapper.getImcContractExpenseVoById(item.getContractExpenseId());
                        if (contractExpenseVo.getIsBaseFeeRate().equals(YesOrNoEnum.Y.name())) {
                            imcOrderRisk.setIsFeeSettled(YesOrNoEnum.Y.name());
                            imcOrderRisk.setSettleMonth(imcRecn.getSettleMonth().substring(0, 7));
                            imcOrderRisk.setUpdateId(SecurityUtils.getUsername());
                            imcOrderRisk.setUpdateTime(DateUtil.date());
                            imcOrderRiskList.add(imcOrderRisk);
                        }
                    } else {
                        logger.info("首年手续费审核通过未修改ImcOrderRisk " + "保单号：" + item.getPolicyCode() + " OrderRiskId：" + item.getOrderRiskId());
                    }
                    // 计算总手续费、总保费
                    if (item.getRecnHandlingResult().equals(RecnHandlingResultEnum.B.getCode())) {
                        totalFee = totalFee.add(item.getFee());
                        totalPrem = totalPrem.add(item.getPrem());
                    }
                }
            }
            if (CollUtil.isNotEmpty(imcOrderRiskList)) {
                this.imcOrderRiskMapper.updateImcOrderRiskListOfRecn(imcOrderRiskList);
            }
        }
        List<ImcRecnSupplierDetails> sList = this.imcRecnSupplierDetailsMapper.selectImcRecnSupplierDetailsByRecnId(recnId, SecurityUtils.getTenantId());
        if (CollUtil.isNotEmpty(sList)) {
            for (ImcRecnSupplierDetails item : sList) {
                // 计算总手续费、总保费
                if (item.getRecnHandlingResult().equals(RecnHandlingResultEnum.A.getCode())) {
                    totalPrem = totalPrem.add(item.getPrem());
                }
            }
        }

        // 更新主表：结算状态->已完成
        imcRecn.setApprovedComments(recnSearchForm.getApprovedComments());
        imcRecn.setTotalFee(totalFee);
        imcRecn.setTotalPrem(totalPrem);
        imcRecn.setRecnStatus(RecnStatusEnum.D.getCode());
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        this.imcRecnMapper.updateImcRecn(imcRecn);
        return AjaxResult.success();
    }

    /**
     * 退回
     *
     * @param recnId         主表ID
     * @param recnSearchForm RecnSearchForm
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult auditReturn(Long recnId, RecnSearchForm recnSearchForm) {
        logger.info("手续费账单-退回：{}", recnId);

        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isNull(imcRecn)) {
            return AjaxResult.error("该手续费对账单不存在");
        }
        if (RecnStatusEnum.D.getCode().equals(imcRecn.getRecnStatus())) {
            return AjaxResult.error("该手续费对账单状态为已完成，无法操作退回，请进行反结");
        }
        // 更新主表：结算状态->进行中
        imcRecn.setReturnComments(recnSearchForm.getReturnComments());
        imcRecn.setTotalFee(new BigDecimal("0.0"));
        imcRecn.setTotalPrem(new BigDecimal("0.0"));
        imcRecn.setRecnStatus(RecnStatusEnum.A.getCode());
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        this.imcRecnMapper.updateImcRecn(imcRecn);
        return AjaxResult.success();
    }

    /**
     * 反结
     *
     * @param recnId         主表ID
     * @param recnSearchForm RecnSearchForm
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult auditRevert(Long recnId, RecnSearchForm recnSearchForm) {
        logger.info("手续费账单-反结：{}", recnId);
        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isNull(imcRecn)) {
            return AjaxResult.error("该手续费对账单不存在");
            // 校验：是否存在长账情况
        }
        boolean isExistSl = this.imcRecnSupplierDetailsMapper.countOfSupplierDetailsByRecnId(recnId, RecnCheckResultEnum.SL.getCode(), SecurityUtils.getTenantId()) > 0;
        if (isExistSl) {
            return AjaxResult.error("该手续费对账单存在长账，请处理后再反结");
        }
        // 更新[order_risk]表：手续费是否结算状态=>未结算
        List<ImcRecnDetails> lList = this.imcRecnDetailsMapper.selectImcRecnDetailsByRecnId(recnId, SecurityUtils.getTenantId());
        List<ImcOrderRisk> imcOrderRiskList = CollUtil.newArrayList();
        for (ImcRecnDetails item : lList) {
            ImcOrderRisk imcOrderRisk = this.imcOrderRiskMapper.getImcOrderRiskById(item.getOrderRiskId());
            // 费用科目中配置的基础手续费为是时，更新险种手续费是否结算状态
            ContractExpenseVo contractExpenseVo = this.imcContractExpenseMapper.getImcContractExpenseVoById(item.getContractExpenseId());
            if (contractExpenseVo.getIsBaseFeeRate().equals(YesOrNoEnum.Y.name())) {
                imcOrderRisk.setIsFeeSettled(YesOrNoEnum.N.name());
                imcOrderRisk.setSettleMonth(null);
                imcOrderRisk.setUpdateId(SecurityUtils.getUsername());
                imcOrderRisk.setUpdateTime(DateUtil.date());
                imcOrderRiskList.add(imcOrderRisk);
            }
        }
        if (CollUtil.isNotEmpty(imcOrderRiskList)) {
            this.imcOrderRiskMapper.updateImcOrderRiskListOfRecn(imcOrderRiskList);
        }

        // 更新主表：结算状态=>进行中
        imcRecn.setRevertComments(recnSearchForm.getRevertComments());
        imcRecn.setTotalFee(new BigDecimal("0.0"));
        imcRecn.setTotalPrem(new BigDecimal("0.0"));
        imcRecn.setRecnStatus(RecnStatusEnum.A.getCode());
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        this.imcRecnMapper.updateImcRecn(imcRecn);
        return AjaxResult.success();
    }

    /**
     * 删除
     * <p>
     * 1.逻辑删除主表、L表和S表
     * 2.将[order_risk]表的结算年月置空、
     *
     * @param recnId 主表ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult deleteImcRecnById(Long recnId) {
        logger.info("首年手续费对账单-删除：{}", recnId);
        // 逻辑删除主表
        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isNull(imcRecn)) {
            return AjaxResult.error("该手续费对账单不存在");
        }
        if (imcRecn.getRecnStatus().equals(RecnStatusEnum.D.getCode())) {
            return AjaxResult.error("该手续费账单结算已完成，不允许删除");
        }
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        this.imcRecnMapper.deleteImcRecn(imcRecn);

        // 逻辑删除L表、更新[order_risk]表
        List<ImcRecnDetails> lList = this.imcRecnDetailsMapper.selectImcRecnDetailsByRecnId(recnId, SecurityUtils.getTenantId());
        List<ImcOrderRisk> imcOrderRiskList = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(lList)) {
            for (ImcRecnDetails item : lList) {
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
                if (ObjectUtil.isNotNull(item.getOrderRiskId())) {
                    ImcOrderRisk imcOrderRisk = this.imcOrderRiskMapper.getImcOrderRiskById(item.getOrderRiskId());
                    // 费用科目中配置为基础手续费率时，更新手续费是否结算状态
                    ContractExpenseVo contractExpenseVo = this.imcContractExpenseMapper.getImcContractExpenseVoById(item.getContractExpenseId());
                    if (contractExpenseVo.getIsBaseFeeRate().equals(YesOrNoEnum.Y.name())) {
                        imcOrderRisk.setIsFeeSettled(YesOrNoEnum.N.name());
                        imcOrderRisk.setSettleMonth(null);
                        imcOrderRisk.setUpdateId(SecurityUtils.getUsername());
                        imcOrderRisk.setUpdateTime(DateUtil.date());
                        imcOrderRiskList.add(imcOrderRisk);
                    }
                }
            }
            this.imcRecnDetailsMapper.updateImcRecnDetailsListOfDelete(lList);
            if (CollUtil.isNotEmpty(imcOrderRiskList)) {
                //反结操作已经把状态置位N 删除操作不需要再操作这个表的状态
                //this.imcOrderRiskMapper.updateImcOrderRiskListOfRecn(imcOrderRiskList);
            }
        }

        // 逻辑删除S表
        List<ImcRecnSupplierDetails> sList = this.imcRecnSupplierDetailsMapper.selectImcRecnSupplierDetailsByRecnId(recnId, SecurityUtils.getTenantId());
        if (CollUtil.isNotEmpty(sList)) {
            for (ImcRecnSupplierDetails item : sList) {
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
            }
            this.imcRecnSupplierDetailsMapper.updateImcRecnSupplierDetailsListOfDelete(sList);
        }

        return AjaxResult.success();
    }

    /**
     * 写入S表
     *
     * @param recnId       主表ID
     * @param settleMonth  结算年月（yyyy-MM）
     * @param supplierId   供应商ID
     * @param templateList 模板文件
     * @return 结果
     */
    private Map<String, Object> importRecnSupplierDetails(Long recnId, String settleMonth, Long supplierId, List<RecnTemplate> templateList) {
        logger.info("供应商明细数据上传，上传数据：{}条", templateList.size());
        // 重新上传，则逻辑删除原数据
        List<ImcRecnSupplierDetails> sList = imcRecnSupplierDetailsMapper.selectImcRecnSupplierDetailsByRecnId(recnId, SecurityUtils.getTenantId());
        if (CollUtil.isNotEmpty(sList)) {
            for (ImcRecnSupplierDetails item : sList) {
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
            }
            imcRecnSupplierDetailsMapper.updateImcRecnSupplierDetailsListOfDelete(sList);
        }

        // 写入S表，返回错误信息提示
        int successNum = 0;
        int failureNum = 0;
        Map<String, Object> rtnMap = CollUtil.newHashMap();
        List<RecnTemplate> errorMsgList = CollUtil.newArrayList();
        List<ImcRecnSupplierDetails> detailsList = CollUtil.newArrayList();

        for (RecnTemplate template : templateList) {
            // 数据校验
            StringBuilder errorMsg = importParameterVerify(template, supplierId);
            ImcRecnSupplierDetails details = new ImcRecnSupplierDetails();
            BeanUtils.copyProperties(template, details);
            if (errorMsg.length() > 0) {
                template.setErrorMsg(errorMsg.toString());
                errorMsgList.add(template);
                failureNum++;
            } else {
                // 保单信息
                details.setPolicyCode(template.getPolicyCodeS().trim());
                ImcOrder order = imcOrderMapper.getImcOrderByPolicyCode(template.getPolicyCodeS().trim(), SecurityUtils.getTenantId());
                details.setOrderId(order.getId());
                // 险种信息
                details.setInsCode(template.getInsCodeS().trim());
                ImcRepoIns imcRepoIns = this.imcRepoInsMapper.getImcRepoInsBySupplierInsCode(template.getInsCodeS().trim(), supplierId);
                details.setRepoInsId(imcRepoIns.getId());
                details.setInsCode(template.getInsCodeS().trim());
                details.setRiskName(template.getRiskNameS().trim());
                // 对账信息
                details.setRecnId(recnId);
                details.setInsureDate(template.getInsureDateS());
                details.setCustAckDate(template.getCustAckDateS());
                details.setChargePeriodValue(template.getChargePeriodValueS().trim());
                details.setPolicyYear(template.getPolicyYearS());
                details.setPayNum(template.getPayNumS());
                details.setSettleDate(DateUtils.getNowDate());
                details.setSettleMonth(settleMonth);
                details.setPrem(template.getPremS());
                details.setRecnCheckResult("NULL");
                details.setRecnHandlingResult(RecnHandlingResultEnum.A.getCode());
                details.setCreateTime(DateUtil.date());
                details.setCreateId(SecurityUtils.getUsername());
                details.setUpdateTime(DateUtil.date());
                details.setUpdateId(SecurityUtils.getUsername());
                details.setTenantId(SecurityUtils.getTenantId());
                details.setIsDeleted(IsDeletedEnum.N.getCode());
                detailsList.add(details);
            }
        }
        for (ImcRecnSupplierDetails item : detailsList) {
            if (failureNum == 0) {
                this.imcRecnSupplierDetailsMapper.insertImcRecnSupplierDetails(item);
                successNum++;
            }
        }
        if (failureNum > 0) {
            rtnMap.put(RecnFileStatusEnum.FAIL.getCode(), errorMsgList);
        } else {
            rtnMap.put(RecnFileStatusEnum.SUCCESS.getCode(), successNum);
        }
        return rtnMap;
    }

    /**
     * 校验：上传数据完整性&有效性
     *
     * @param template   上传数据
     * @param supplierId 供应商id
     * @return 结果
     */
    private StringBuilder importParameterVerify(RecnTemplate template, Long supplierId) {
        StringBuilder errorMsg = new StringBuilder();
        if (StringUtils.isBlank(template.getPolicyCodeS().trim())) {
            errorMsg.append("保单号不能为空；");
        } else {
            ImcOrder imcOrder = this.imcOrderMapper.getImcOrderByPolicyCode(template.getPolicyCodeS().trim(), SecurityUtils.getTenantId());
            if (ObjectUtil.isNull(imcOrder)) {
                errorMsg.append("该保单在系统内不存在；");
            }
        }
        if (StringUtils.isBlank(template.getInsCodeS().trim())) {
            errorMsg.append("险种代码不能为空；");
        } else {
            ImcRepoIns imcRepoIns = this.imcRepoInsMapper.getImcRepoInsBySupplierInsCode(template.getInsCodeS().trim(), supplierId);
            if (ObjectUtil.isNull(imcRepoIns)) {
                errorMsg.append("该保单的险种对应的供应商与手续费对账清单列表中的供应商不一致，请检查；");
            }
        }
        if (ObjectUtil.isNull(template.getPayNumS())) {
            errorMsg.append("保单缴费期数不能为空；");
        }
        if (StringUtils.isBlank(template.getChargePeriodValueS().trim())) {
            errorMsg.append("缴费期限不能为空；");
        }
        if (ObjectUtil.isNull(template.getInsureDateS())) {
            errorMsg.append("承保日期不能为空；");
        }
        if (ObjectUtil.isNull(template.getCustAckDateS())) {
            errorMsg.append("客户回执签收时间不能为空；");
        }
        if (ObjectUtil.isNull(template.getPremS())) {
            errorMsg.append("险种保费不能为空；");
        }
        if (ObjectUtil.isNull(template.getPolicyYearS())) {
            errorMsg.append("保单所在年度不能为空；");
        }
        return errorMsg;
    }

    /**
     * 核对：平账（L_SUCCESS）数据处理
     * <p>
     * 手续费核对结果：核对项相等即为平账，并非手续费金额相等为平账
     * 手续费处理结果：默认以供应商为准
     * 对账项：
     * 1.保单号
     * 2.险种ID
     * 3.缴费期数
     * 4.缴费期限
     * 5.承保日期
     * 6.客户回执签收日期
     * 7.保费
     * 8.保单年度
     *
     * @param recnId 主表ID
     * @param lList  L表数据
     * @param sList  S表数据
     */
    private void checkOfLsSuccess(Long recnId, List<RecnDetailsVo> lList, List<RecnDetailsVo> sList) {
        // L=>S表
        List<RecnDetailsVo> unionLsList = lList.stream().filter(item -> sList.stream()
                .map(e -> e.getPolicyCode() + "@@"
                        + e.getRepoInsId() + "@@"
                        + e.getPayNum() + "@@"
                        + e.getChargePeriodValue() + "@@"
                        + e.getInsureDate() + "@@"
                        + e.getCustAckDate() + "@@"
                        + e.getPrem() + "@@"
                        + e.getPolicyYear()).collect(Collectors.toList())
                .contains(item.getPolicyCode() + "@@"
                        + item.getRepoInsId() + "@@"
                        + item.getPayNum() + "@@"
                        + item.getChargePeriodValue() + "@@"
                        + item.getInsureDate() + "@@"
                        + item.getCustAckDate() + "@@"
                        + item.getPrem() + "@@"
                        + item.getPolicyYear())).collect(Collectors.toList());
        // S=>L表
        List<RecnDetailsVo> unionSlList = sList.stream().filter(item -> lList.stream()
                .map(e -> e.getPolicyCode() + "@@"
                        + e.getRepoInsId() + "@@"
                        + e.getPayNum() + "@@"
                        + e.getChargePeriodValue() + "@@"
                        + e.getInsureDate() + "@@"
                        + e.getCustAckDate() + "@@"
                        + e.getPrem() + "@@"
                        + e.getPolicyYear()).collect(Collectors.toList())
                .contains(item.getPolicyCode() + "@@"
                        + item.getRepoInsId() + "@@"
                        + item.getPayNum() + "@@"
                        + item.getChargePeriodValue() + "@@"
                        + item.getInsureDate() + "@@"
                        + item.getCustAckDate() + "@@"
                        + item.getPrem() + "@@"
                        + item.getPolicyYear())).collect(Collectors.toList());
        // 更新L表
        if (CollUtil.isNotEmpty(unionLsList)) {
            for (RecnDetailsVo item : unionLsList) {
                List<RecnDetailsVo> slSuccessList = unionSlList.stream()
                        .filter(e -> e.getPolicyCode().equals(item.getPolicyCode())
                                && e.getRepoInsId().equals(item.getRepoInsId())
                                && e.getPayNum().equals(item.getPayNum())
                                && e.getChargePeriodValue().equals(item.getChargePeriodValue())
                                && e.getInsureDate().equals(item.getInsureDate())
                                && e.getCustAckDate().equals(item.getCustAckDate())
                                && e.getPrem().equals(item.getPrem())
                                && e.getPolicyYear().equals(item.getPolicyYear())).collect(Collectors.toList());
                RecnDetailsVo sl = slSuccessList.get(0);
                item.setRecnId(recnId);
                item.setRecnSupplierDetailsId(sl.getId());
                item.setRecnCheckResult(RecnCheckResultEnum.L_SUCCESS.getCode());
                item.setRecnHandlingResult(RecnHandlingResultEnum.A.getCode());
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
            }
            this.imcRecnDetailsMapper.updateDetailsListOfCheckResult(unionLsList);
        }
        BigDecimal totalFee = new BigDecimal("0.0");
        BigDecimal totalPrem = new BigDecimal("0.0");
        // 更新S表
        if (CollUtil.isNotEmpty(unionSlList)) {
            for (RecnDetailsVo item : unionSlList) {
                item.setRecnCheckResult(RecnCheckResultEnum.L_SUCCESS.getCode());
                item.setRecnHandlingResult(RecnHandlingResultEnum.A.getCode());
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
                totalPrem = totalPrem.add(item.getPrem());
            }
            this.imcRecnSupplierDetailsMapper.updateSupplierDetailsListOfCheckResult(unionSlList);
        }
        // 更新主表
        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        imcRecn.setTotalFee(totalFee);
        imcRecn.setTotalPrem(totalPrem);
        imcRecn.setUpdateId(SecurityUtils.getUsername());
        imcRecn.setUpdateTime(DateUtil.date());
        this.imcRecnMapper.updateImcRecn(imcRecn);
    }

    /**
     * 核对：短账（LS）数据处理
     * <p>
     * 手续费核对结果：短账
     * 手续费处理结果：忽略不处理
     * 对账项：
     * 1.保单号
     * 2.险种ID
     * 3.缴费期数
     * 4.缴费期限
     * 5.承保日期
     * 6.客户回执签收日期
     * 7.保费
     * 8.保单年度
     *
     * @param recnId 主表ID
     * @param lList  L表数据
     * @param sList  S表数据
     */
    private void checkOfLs(Long recnId, List<RecnDetailsVo> lList, List<RecnDetailsVo> sList) {
        List<RecnDetailsVo> lsRecnList = lList.stream().filter(item -> !sList.stream()
                .map(e -> e.getPolicyCode() + "@@"
                        + e.getRepoInsId() + "@@"
                        + e.getPayNum() + "@@"
                        + e.getChargePeriodValue() + "@@"
                        + e.getInsureDate() + "@@"
                        + e.getCustAckDate() + "@@"
                        + e.getPrem() + "@@"
                        + e.getPolicyYear()).collect(Collectors.toList())
                .contains(item.getPolicyCode() + "@@"
                        + item.getRepoInsId() + "@@"
                        + item.getPayNum() + "@@"
                        + item.getChargePeriodValue() + "@@"
                        + item.getInsureDate() + "@@"
                        + item.getCustAckDate() + "@@"
                        + item.getPrem() + "@@"
                        + item.getPolicyYear())).collect(Collectors.toList());
        // 更新L表
        if (CollUtil.isNotEmpty(lsRecnList)) {
            for (RecnDetailsVo item : lsRecnList) {
                item.setRecnId(recnId);
                item.setRecnCheckResult(RecnCheckResultEnum.LS.getCode());
                item.setRecnHandlingResult(RecnHandlingResultEnum.C.getCode());
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
            }
            this.imcRecnDetailsMapper.updateDetailsListOfCheckResult(lsRecnList);
        }
    }

    /**
     * 核对：长账（SL）数据处理
     * <p>
     * 手续费核对结果：长账
     * 手续费处理结果：忽略不处理
     * 对账项：
     * 1.保单号
     * 2.险种ID
     * 3.缴费期数
     * 4.缴费期限
     * 5.承保日期
     * 6.客户回执签收日期
     * 7.保费
     * 8.保单年度
     *
     * @param lList L表数据
     * @param sList S表数据
     */
    private void checkOfSl(List<RecnDetailsVo> lList, List<RecnDetailsVo> sList) {
        List<RecnDetailsVo> slRecnList = sList.stream().filter(item -> !lList.stream()
                .map(e -> e.getPolicyCode() + "@@"
                        + e.getInsCode() + "@@"
                        + e.getPayNum() + "@@"
                        + e.getChargePeriodValue() + "@@"
                        + e.getInsureDate() + "@@"
                        + e.getCustAckDate() + "@@"
                        + e.getPrem() + "@@"
                        + e.getPolicyYear()).collect(Collectors.toList())
                .contains(item.getPolicyCode() + "@@"
                        + item.getInsCode() + "@@"
                        + item.getPayNum() + "@@"
                        + item.getChargePeriodValue() + "@@"
                        + item.getInsureDate() + "@@"
                        + item.getCustAckDate() + "@@"
                        + item.getPrem() + "@@"
                        + item.getPolicyYear())).collect(Collectors.toList());
        // 更新S表
        if (CollUtil.isNotEmpty(slRecnList)) {
            for (RecnDetailsVo item : slRecnList) {
                item.setRecnCheckResult(RecnCheckResultEnum.SL.getCode());
                item.setRecnHandlingResult(RecnHandlingResultEnum.C.getCode());
                item.setUpdateId(SecurityUtils.getUsername());
                item.setUpdateTime(DateUtil.date());
            }
            this.imcRecnSupplierDetailsMapper.updateSupplierDetailsListOfCheckResult(slRecnList);
        }
    }

    /**
     * 转换：行转列
     * <p>
     * 1.数据处理
     * 2.数据行转列
     * 3.数据拼装
     *
     * @param beforeTransList 转换前的手续费核算结果集合
     * @return 转换后的手续费核算结果集合
     */
    private List<RecnDetailsCheckVo> rowTransColumn(List<RecnDetailsCheckVo> beforeTransList) {
        //  数据处理（以orderRiskId为唯一标识进行数据分组、生成动态列）
        //  =>{{orderRiskIdA,[dynamicFieldNameA1,dynamicFieldNameA2],[dynamicFieldValueA1,dynamicFieldValueA2]},{orderRiskIdB,[dynamicFieldNameB1,dynamicFieldNameB2],[dynamicFieldValueB1,dynamicFieldValueB2]}}
        List<List<RecnExpenseDynamicField>> beforeDynamicFieldList = beforeTransList.stream()
                .collect(Collectors.groupingBy(RecnDetailsCheckVo::getOrderRiskId))
                .entrySet()
                .stream()
                .map(entry -> {
                    // 动态列的字段名（唯一标识+协议费用科目id）
                    // =>{orderRiskIdA,[dynamicFieldNameA1,dynamicFieldNameA2]}
                    List<Object> dynamicFieldNameList = CollUtil.newArrayList();
                    dynamicFieldNameList.add(entry.getKey());
                    dynamicFieldNameList.addAll((entry.getValue().stream().map(RecnDetailsCheckVo::getContractExpenseId).collect(Collectors.toList())));

                    // 动态列的字段值（唯一标识+协议费用科目费用）
                    // =>{orderRiskIdA,[dynamicFieldValueA1,dynamicFieldValueA2]}
                    List<Object> dynamicFieldValueList = CollUtil.newArrayList();
                    dynamicFieldValueList.add(entry.getKey());
                    dynamicFieldValueList.addAll(entry.getValue().stream().map(RecnDetailsCheckVo::getFeeL).collect(Collectors.toList()));

                    // 保存动态列（动态列的字段名+字段值）
                    List<RecnExpenseDynamicField> recnExpenseDynamicFieldList = CollUtil.newArrayList();
                    RecnExpenseDynamicField recnExpenseDynamicField = new RecnExpenseDynamicField();
                    recnExpenseDynamicField.setDynamicFieldNameList(dynamicFieldNameList);
                    recnExpenseDynamicField.setDynamicFieldValueList(dynamicFieldValueList);
                    recnExpenseDynamicFieldList.add(recnExpenseDynamicField);
                    return recnExpenseDynamicFieldList;
                }).collect(Collectors.toList());

        // 数据行转列（以orderRiskId为唯一标识进行行转列）
        // =>{{orderRiskIdA,[{dynamicFieldNameA1:dynamicFieldValueA1},{dynamicFieldNameA2:dynamicFieldValueB2}]}}
        List<RecnExpenseDynamicField> afterDynamicFieldList = CollUtil.newArrayList();
        for (List<RecnExpenseDynamicField> itemList : beforeDynamicFieldList) {
            List<Object> dynamicFieldNameList = itemList.get(0).getDynamicFieldNameList();
            List<Object> dynamicFieldValueList = itemList.get(0).getDynamicFieldValueList();

            RecnExpenseDynamicField dynamicField = new RecnExpenseDynamicField();
            dynamicField.setOrderRiskId((Long) dynamicFieldNameList.get(0));

            Map<String, Object> dynamicFieldMap = CollUtil.newHashMap();
            for (int i = 1; i < dynamicFieldNameList.size(); i++) {
                dynamicFieldMap.put(dynamicFieldNameList.get(i).toString(), dynamicFieldValueList.get(i).toString());
                dynamicField.setDynamicFieldMap(dynamicFieldMap);
            }
            afterDynamicFieldList.add(dynamicField);
        }


        // 数据拼装
        List<RecnDetailsCheckVo> afterTransList = CollUtil.newArrayList();
        Map<String, Object> reMap = CollUtil.newHashMap();
        for (RecnDetailsCheckVo item : beforeTransList) {
            reMap.put(item.getOrderRiskId().toString(), item);
        }
        for (RecnExpenseDynamicField it : afterDynamicFieldList) {
            RecnDetailsCheckVo recnDetailsCheckVo = (RecnDetailsCheckVo) reMap.get(it.getOrderRiskId().toString());
            recnDetailsCheckVo.setDynamicFieldMap(it.getDynamicFieldMap());
            afterTransList.add(recnDetailsCheckVo);
        }

        return afterTransList;
    }

    /**
     * 设置查询费率的条件
     *
     * @param item ImcRecnDetails
     * @return QueryWrapper<ImcContractExpenseRate>
     */
    private QueryWrapper<ImcContractExpenseRate> setCheckWrapper(ImcRecnDetails item) {
        QueryWrapper<ImcContractExpenseRate> checkWrapper = new QueryWrapper<>();
        checkWrapper.eq("repo_ins_id", item.getRepoInsId());
        checkWrapper.eq("contract_expense_id", item.getContractExpenseId());
        checkWrapper.eq("is_deleted", IsDeletedEnum.N.getCode());
        if (StringUtils.isNotBlank(item.getChargePeriodValue())) {
            checkWrapper.le("charge_period_lower_value", item.getChargePeriodValue());
            checkWrapper.ge("charge_period_upper_value", item.getChargePeriodValue());
        }
        if (StringUtils.isNotBlank(item.getCoveragePeriodValue())) {
            checkWrapper.le("coverage_period_lower_value", item.getCoveragePeriodValue());
            checkWrapper.ge("coverage_period_upper_value", item.getCoveragePeriodValue());
        }
        if (item.getApplyDate() != null) {
            checkWrapper.le("start_date", item.getApplyDate());
            checkWrapper.ge("end_date", item.getApplyDate());
        }
        if (StringUtils.isNotBlank(item.getChargeIntervalEnum())) {
            checkWrapper.like("charge_interval_enum", item.getChargeIntervalEnum());
        }
        if (item.getPensionAge() != null) {
            checkWrapper.le("pension_age_lower", item.getPensionAge());
            checkWrapper.ge("pension_age_upper", item.getPensionAge());
        }
        if (item.getPolicyYear() != null) {
            checkWrapper.le("pay_year_lower", item.getPolicyYear());
            checkWrapper.ge("pay_year_upper", item.getPolicyYear());
        }
        if (item.getInsSchemeId() != null) {
            checkWrapper.eq("ins_scheme_id", item.getInsSchemeId());
        }
        if (StringUtils.isNotBlank(item.getFactor1Value())) {
            checkWrapper.eq("factor1_value", item.getFactor1Value());
        }
        if (StringUtils.isNotBlank(item.getFactor2Value())) {
            checkWrapper.eq("factor2_value", item.getFactor2Value());
        }
        if (StringUtils.isNotBlank(item.getFactor3Value())) {
            checkWrapper.eq("factor3_value", item.getFactor3Value());
        }
        return checkWrapper;
    }
}
